/*
The surface-physics project: http://code.google.com/p/surface-physics/
Adds real world physics based on the Bullet physics engine, to bases in the Orbiter space
flight simulation software.

Written by Abhijit Nandy
License : Apache License 2.0

Bump_ThreadBarrier.h
Defines a thread barrier class to synchronize all threads at every sim step.

*/


#pragma once

const unsigned long MAX_THREADS_AT_BARRIER = 4096;

class Bump_ThreadBarrier {

	volatile long numThreads;

	volatile long numThreadsEntered_Barrier;
	volatile long numThreadsExited_Barrier;
	HANDLE entranceSemaphore;
	HANDLE exitSemaphore;



public:
	Bump_ThreadBarrier(int numThreadsToSync) :
		numThreads(numThreadsToSync),
		numThreadsEntered_Barrier(0),
		numThreadsExited_Barrier(0)
	{
		entranceSemaphore = CreateSemaphore(NULL, 0, MAX_THREADS_AT_BARRIER, NULL);
		exitSemaphore = CreateSemaphore(NULL, 0, MAX_THREADS_AT_BARRIER, NULL);
	}

	~Bump_ThreadBarrier()
	{ }

	void wait(){

		LONG prev;


		// Wait until all threads enter the Barrier
		if (InterlockedIncrement(&numThreadsEntered_Barrier) < numThreads)
			WaitForSingleObject(entranceSemaphore, INFINITE);
		else {
			numThreadsExited_Barrier = 0;
			 //single_thread_callback();
			ReleaseSemaphore(entranceSemaphore, numThreads-1, &prev);
		}

		//all_threads_callback();


		// Wait until all threads exit the Barrier
		if (InterlockedIncrement(&numThreadsExited_Barrier) < numThreads)
			WaitForSingleObject(exitSemaphore, INFINITE);		//the actual pausing mechanism which holds n-1 threads
		else {
			numThreadsEntered_Barrier = 0;
			ReleaseSemaphore(exitSemaphore, numThreads-1, &prev); //nth thread to enter,never pauses, it signals all others to continue
							//& continues itself
		}



	}

	void waitTillNThreadsBlocked(int n)
	{
		while(numThreadsEntered_Barrier < n)
			Sleep(10);
	}

	void releaseAll()
	{
		LONG prev1, prev2;

		//Both sem. must be made max, as, if sem drops to 0(as wait release threads) be4 all threads are released,
		//remaining threads remain halted as sem is 0, as threads wait if sem = 0
		ReleaseSemaphore(entranceSemaphore, MAX_THREADS_AT_BARRIER, &prev1);
		ReleaseSemaphore(exitSemaphore, MAX_THREADS_AT_BARRIER, &prev2);

		numThreadsEntered_Barrier = 0;
		numThreadsExited_Barrier = 0;
	}

	void setMaxCount(long numThreadsToSync)
	{ numThreads = numThreadsToSync; }

	long  getMaxCount()
	{ return numThreads; };
};


